<?php

namespace Hong\Webdav;

require_once __DIR__ . '/options.php';
require_once __DIR__ . '/get.php';
require_once __DIR__ . '/put.php';
require_once __DIR__ . '/propfind.php';
require_once __DIR__ . '/delete.php';
require_once __DIR__ . '/fileCrc32.php';

use Hong\Webdav\TraitOptions;
use Hong\Webdav\TraitGet;
use Hong\Webdav\TraitPut;
use Hong\Webdav\TraitPropfind;
use Hong\Webdav\TraitDelete;
use Hong\Webdav\TraitFileCrc32;

class Webdav
{
	private $baseUri;
	private $user;
	private $password;
	private $serverCapabilities = null; // 缓存服务器能力

	use TraitOptions;	// 服务端提供的能力 返回字符串表示
	use TraitGet;		// 下载文件
	use TraitPut;		// 上传文件
	use TraitPropfind;	// 资源信息 目录、文件信息
	use TraitDelete;	// 删除目录or文件
	use TraitFileCrc32;	// 计算本地文件的crc32校验码

	/**
	 * 构造函数
	 * @param string $baseUri WebDAV服务器基础URI
	 * @param string $user 用户名
	 * @param string $password 密码
	 */
	public function __construct($baseUri, $baseDir, $user, $password)
	{
		$this->baseUri = rtrim($baseUri, '/');
		$this->baseUri .= '/' . ltrim($baseDir, '/');
		$this->user = $user;
		$this->password = $password;
	}

	/**
	 * 解析PROPFIND响应XML
	 * @param string $xmlResponse PROPFIND响应XML
	 * @return array 解析后的属性数组
	 */
	private function parsePropfindResponse($xmlResponse)
	{
		$dom = new \DOMDocument(); // 添加反斜杠表示全局命名空间
		if (!$dom->loadXML($xmlResponse)) {
			throw new \RuntimeException("Failed to parse PROPFIND response XML");
		}

		$results = [];
		$xpath = new \DOMXPath($dom);

		// 注册DAV命名空间（即使XML中使用的是D:前缀）
		$xpath->registerNamespace('d', 'DAV:');

		// 查询所有response节点
		$responses = $xpath->query('//d:response');

		foreach ($responses as $response) {
			// 获取href
			$hrefNodes = $xpath->query('d:href', $response);
			$href = $hrefNodes->length > 0 ? trim($hrefNodes->item(0)->nodeValue) : '';

			$props = [];

			// 查询所有propstat节点
			$propstats = $xpath->query('d:propstat', $response);

			foreach ($propstats as $propstat) {
				// 检查状态
				$statusNodes = $xpath->query('d:status', $propstat);
				$status = $statusNodes->length > 0 ? $statusNodes->item(0)->nodeValue : '';

				if (strpos($status, '200 OK') !== false) {
					// 查询prop节点下的所有属性
					$propNodes = $xpath->query('d:prop/*', $propstat);

					foreach ($propNodes as $propNode) {
						$name = $propNode->localName;

						// 处理resourcetype
						if ($name === 'resourcetype') {
							$props[$name] = [];
							foreach ($xpath->query('*', $propNode) as $child) {
								$props[$name][] = $child->localName;
							}
						} else {
							$props[$name] = $propNode->nodeValue;
						}
					}
				}
			}
			if (!empty($href)) {
				$results[$href] = $props;
			}
		}
		return $results;
	}

	/**
	 * 发送WebDAV请求
	 * @param string $method 请求方法
	 * @param string $path 路径
	 * @param mixed $data 数据(字符串内容或文件路径)
	 * @param array $additionalHeaders 额外头部
	 * @return array 响应数组
	 * @throws \RuntimeException 当请求失败时抛出异常
	 */
	private function sendRequest($method, $path, $data = null, array $additionalHeaders = [])
	{
		$uri = rtrim($this->baseUri, '/') . '/' . ltrim($path, '/');

		$ch = curl_init();
		curl_setopt($ch, CURLOPT_URL, $uri);
		curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
		curl_setopt($ch, CURLOPT_USERPWD, $this->user . ':' . $this->password);
		curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST); // CURLAUTH_DIGEST CURLAUTH_BASIC
		curl_setopt($ch, CURLOPT_HEADER, true); // 获取头部

		// 设置额外头部
		if($method == "PUT"){
			$additionalHeaders[] = "Expect:";
		}
		if (!empty($additionalHeaders)) {
			curl_setopt($ch, CURLOPT_HTTPHEADER, $additionalHeaders);
		}

		// 动态计算超时时间（单位：秒）
		$timeout = 30; // 默认基础超时
		$sizeFactor = 0; // 基于数据大小的额外时间

		// 携带数据请求 处理数据或文件上传
		if ($data !== null && in_array($method, ['PUT', 'PROPFIND'])) {
			if (is_string($data) && file_exists($data)) {
				// $data是文件路径
				$fileHandle = fopen($data, 'r');
				if ($fileHandle === false) {
					throw new \RuntimeException("Failed to open file: " . $data);
				}

				curl_setopt($ch, CURLOPT_PUT, true);
				curl_setopt($ch, CURLOPT_INFILE, $fileHandle);
				curl_setopt($ch, CURLOPT_INFILESIZE, filesize($data));
				// 文件大小（MB） 大概每秒100k 上传大文件时超时时间适当加宽些
				$fileSizeMB = filesize($data) / (100 * 1024);
				$sizeFactor = $fileSizeMB * 1;  // 1秒1份
			} else {
				// $data是字符串内容
				curl_setopt($ch, CURLOPT_POSTFIELDS, $data);

				// 如果是PUT请求且不是文件上传，需要设置Content-Length
				if ($method === 'PUT') {
					$additionalHeaders[] = 'Content-Length: ' . strlen($data);
					curl_setopt($ch, CURLOPT_HTTPHEADER, $additionalHeaders);
				}
			}
		}

		// 设置超时（最少30秒，最大300秒）
		$totalTimeout = min(max($timeout + $sizeFactor, 30), 300);
		curl_setopt($ch, CURLOPT_TIMEOUT, $totalTimeout);

		$response = curl_exec($ch);
		$statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
		$error = curl_error($ch);

		// 关闭文件句柄(如果打开了)
		if (isset($fileHandle) && is_resource($fileHandle)) {
			fclose($fileHandle);
		}

		// 分离头部和主体
		$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
		$headers = substr($response, 0, $headerSize);
		$body = substr($response, $headerSize);

		curl_close($ch);

		if ($response === false) {
			throw new \RuntimeException("WebDAV request failed: " . $error);
		}

		// 解析头部为关联数组
		$headerLines = explode("\r\n", trim($headers));
		$parsedHeaders = [];
		foreach ($headerLines as $line) {
			if (strpos($line, ':') !== false) {
				list($name, $value) = explode(':', $line, 2);
				$parsedHeaders[strtolower(trim($name))] = trim($value);
			}
		}

		return [
			'headers' => $parsedHeaders,
			'body' => $body,
			'status' => $statusCode
		];
	}
}
